package la.bbs558.aop.delay;

import com.alibaba.fastjson.JSON;
import com.google.common.primitives.Ints;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Classname DelayTask
 * @Description TODO
 * @Date 2023/11/8 14:18
 * @Created by weihanmin
 */
@Slf4j
public class DelayTask<T extends Serializable> implements Delayed {

    public static final int INITIAL = 0;//初始状态
    public static final int FINISH = 1;//已完成
    public static final int ERROR = -1;//异常
    @Getter
    private final T entity;
    private final long expirationTime;
    private final long instanceTime = System.currentTimeMillis();
    private final TimeUnit convertUnit = TimeUnit.MILLISECONDS;
    @Getter
    @Setter
    private int status = INITIAL;
    @Getter
    private final int maxRetry;
    @Getter
    private final AtomicInteger retry = new AtomicInteger(0);
    @Getter
    private final DelayHandler delayHandler;

    public DelayTask(T entity, long duration, TimeUnit unit, int maxRetry, DelayHandler delayHandler) {
        this.entity = entity;
        this.maxRetry = maxRetry;
        this.expirationTime = convertUnit.convert(duration, unit);
        this.delayHandler = delayHandler;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        long diff = instanceTime + this.expirationTime - System.currentTimeMillis();
        return unit.convert(diff, convertUnit);
    }

    @Override
    public int compareTo(Delayed o) {
        if (o == null) {
            return 0;
        }
        return Ints.saturatedCast(this.expirationTime - ((DelayTask<?>) o).expirationTime);
    }

    public void exec(DelayQueueManager delayQueueManager){
        this.delayHandler.doHandle(this,delayQueueManager);
        this.finish();
    }

    public void execError(DelayQueueManager delayQueueManager,Exception e){
        int retry = this.retry.incrementAndGet();
        this.delayHandler.doHandleError(this,delayQueueManager,e);
        String taskEntityJson = JSON.toJSONString(this.getEntity());
        if(retry < maxRetry){
            delayQueueManager.addTask(this);
            log.warn("taskEntity {} 尝试重新放入延时队列 {}，重试次数 「{}」", taskEntityJson, delayQueueManager.getQueueId(), retry);
        }else {
            this.error();
            log.warn("taskEntity {} 重试次数达到上限「{}」次.丢弃该任务 !! ", taskEntityJson, maxRetry);
        }
    }

    public boolean isError(){
        return getStatus() == ERROR;
    }
    public void error(){
        setStatus(ERROR);
    }
    public boolean isFinish(){
        return getStatus() == FINISH;
    }

    public void finish(){
        setStatus(FINISH);
    }

}
